package com.hzw.saas.web.app.old.storage.aspect;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import com.hzw.saas.api.old.storage.IGroupResourceService;
import com.hzw.saas.api.old.storage.IResourceFileService;
import com.hzw.saas.api.old.storage.IResourceFolderService;
import com.hzw.saas.api.old.storage.bo.ResourceFileBO;
import com.hzw.saas.api.old.storage.bo.ResourceFolderBO;
import com.hzw.saas.api.old.storage.enums.ResourceAuthEnum;
import com.hzw.saas.common.config.util.AssertUtil;
import com.hzw.saas.common.security.pojo.dto.SaasUser;
import com.hzw.saas.common.security.utils.SecurityUtils;
import com.hzw.saas.common.util.SpringELUtils;
import com.hzw.saas.service.old.storage.annotation.FileAuthAfter;
import com.hzw.saas.service.old.storage.annotation.FileAuthBefore;
import com.hzw.saas.service.old.storage.annotation.FolderAuthAfter;
import com.hzw.saas.service.old.storage.annotation.FolderAuthBefore;
import com.hzw.saas.service.old.storage.util.StorageUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;


/**
 * 检查资源权限
 *
 * @author zzl
 * @since 03/07/2021
 */
@Aspect
@Component
@RequiredArgsConstructor
@Slf4j
public class OldResourceAuthCheck {

    private final HttpStatus forbidden = HttpStatus.FORBIDDEN;

    private final IResourceFileService fileService;
    private final IResourceFolderService folderService;
    private final IGroupResourceService groupResourceService;

    @Before("@annotation(fileAuthBefore)")
    @SuppressWarnings("unchecked")
    public void checkFileOwnAuthBeforeInvoke(JoinPoint joinPoint, FileAuthBefore fileAuthBefore) {
        List<String> fileIdList = new ArrayList<>();
        if (StrUtil.isNotBlank(fileAuthBefore.fileId())) {
            String fileId = SpringELUtils.getSpelValue(joinPoint, fileAuthBefore.fileId(), String.class);
            if (StrUtil.isNotBlank(fileId)) {
                fileIdList.add(fileId);
            }
        }
        if (StrUtil.isNotBlank(fileAuthBefore.fileIds())) {
            List<String> fileIds = SpringELUtils.getSpelValue(joinPoint, fileAuthBefore.fileIds(), List.class);
            if (CollectionUtil.isNotEmpty(fileIds)) {
                fileIds.removeIf(StrUtil::isBlank);
                fileIdList.addAll(fileIds);
            }
        }
        for (String fileId : fileIdList) {
            ResourceFileBO file = fileService.getFileInfo(fileId, false);
            this.checkFileAuth(file, fileAuthBefore.auth());
        }
    }

    @AfterReturning(value = "@annotation(fileAuthAfter)", returning = "returnValue")
    public void checkFileOwnAuthAfterInvoke(JoinPoint joinPoint, FileAuthAfter fileAuthAfter, ResponseEntity<ResourceFileBO> returnValue) {
        this.checkFileAuth(returnValue.getBody(), fileAuthAfter.auth());
    }

    @Before("@annotation(folderAuthBefore)")
    @SuppressWarnings("unchecked")
    public void checkFolderOwnAuthBeforeInvoke(JoinPoint joinPoint, FolderAuthBefore folderAuthBefore) {
        List<String> folderIdList = new ArrayList<>();
        if (StrUtil.isNotBlank(folderAuthBefore.folderId())) {
            String folderId = SpringELUtils.getSpelValue(joinPoint, folderAuthBefore.folderId(), String.class);
            if (StrUtil.isNotBlank(folderId) && !StorageUtil.isRootFolder(folderId, false)) {
                folderIdList.add(folderId);
            }
        }
        if (StrUtil.isNotBlank(folderAuthBefore.folderIds())) {
            List<String> folderIds = SpringELUtils.getSpelValue(joinPoint, folderAuthBefore.folderIds(), List.class);
            if (CollectionUtil.isNotEmpty(folderIds)) {
                folderIds.removeIf(s -> StrUtil.isBlank(s) || StorageUtil.isRootFolder(s, false));
                folderIdList.addAll(folderIds);
            }
        }
        for (String folderId : folderIdList) {
            ResourceFolderBO folder = folderService.getFolder(folderId, false);
            this.checkFolderAuth(folder, folderAuthBefore.auth());
        }
    }

    @AfterReturning(value = "@annotation(folderAuthAfter)", returning = "returnValue")
    public void checkFolderOwnAuthAfterInvoke(JoinPoint joinPoint, FolderAuthAfter folderAuthAfter, ResponseEntity<ResourceFolderBO> returnValue) {
        this.checkFolderAuth(returnValue.getBody(), folderAuthAfter.auth());
    }

    private void checkFolderAuth(ResourceFolderBO folderBO, ResourceAuthEnum[] resourceAuths) {
        for (ResourceAuthEnum resourceAuth : resourceAuths) {
            switch (resourceAuth) {
                case OWN:
                    this.checkFolderOwnAuth(folderBO);
                    return;
                case READ:
                    this.checkFolderReadAuth(folderBO);
                    return;
            }
        }
    }

    private void checkFileAuth(ResourceFileBO fileBO, ResourceAuthEnum[] resourceAuths) {
        for (ResourceAuthEnum resourceAuth : resourceAuths) {
            switch (resourceAuth) {
                case OWN:
                    this.checkFileOwnAuth(fileBO);
                    return;
                case READ:
                    this.checkFileReadAuth(fileBO);
                    return;
            }
        }
    }

    private void checkFolderReadAuth(ResourceFolderBO folderBO) {
    }

    private void checkFolderOwnAuth(ResourceFolderBO folderBO) {
        SaasUser saasUser = SecurityUtils.getUser();
        if (folderBO == null)
            return;
        boolean authOwn = Objects.equals(folderBO.getCreatorId(), saasUser.getUserId());
        AssertUtil.assertThrow("无此目录操作权限: " + folderBO.getFolderId(), forbidden, !authOwn);
    }

    private void checkFileReadAuth(ResourceFileBO fileBO) {
        SaasUser saasUser = SecurityUtils.getUser();
        if (fileBO == null)
            return;
        boolean authRead = groupResourceService.checkAuthRead(saasUser.getUserId(), fileBO.getFileId());
        AssertUtil.assertThrow("无此文件读取权限: " + fileBO.getFileId(), forbidden, !authRead);
    }

    private void checkFileOwnAuth(ResourceFileBO fileBO) {
        SaasUser saasUser = SecurityUtils.getUser();
        if (fileBO == null)
            return;
        boolean authOwn = Objects.equals(fileBO.getCreatorId(), saasUser.getUserId());
        AssertUtil.assertThrow("无此文件操作权限: " + fileBO.getFileId(), forbidden, !authOwn);
    }
}
